package net.xinqushi.pool.niopool;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;

/**
 * @author xjie 2019/8/14 17:37
 * @description 网上复制的例子
 */
public class SimpleNioPool {

    /**
     * 主程序
     */
    public static void main(String[] args) throws Exception {
        new NIOSocketBackUp().send(new ServiceRequest());
    }

    /**
     * 配置类
     */
    static class GlobalNames{
        static String industryIP;
        static String industryPort;
    }

    /**
     * NIOConnectionPool 连接池
     */
    static class NIOConnectionPool{
        private  static String IP=GlobalNames.industryIP;
        private  static int PORT =Integer.parseInt(GlobalNames.industryPort);

        private static  int CONNECTION_POOL_SIZE = 10;
        private static NIOConnectionPool self = null;
        private Hashtable<Integer, SocketChannel> socketPool = null; // 连接池
        private boolean[] socketStatusArray = null; // 连接的状态(true-被占用,false-空闲)
        private static Selector selector  = null;
        private static InetSocketAddress SERVER_ADDRESS = null;

        /**
         * 初始化连接池，最大TCP连接的数量为10
         *
         * @throws Exception
         */
        public static synchronized void init() throws Exception {
            self = new NIOConnectionPool();
            self.socketPool = new Hashtable<Integer, SocketChannel>();
            self.socketStatusArray = new boolean[CONNECTION_POOL_SIZE];
            buildConnectionPool();
        }

        /**
         * 建立连接池
         */
        public synchronized static void buildConnectionPool() throws Exception {
            if (self == null) {
                init();
            }
            for (int i = 0; i < CONNECTION_POOL_SIZE; i++) {
                SocketChannel client = allocateSocketChannel();
                self.socketPool.put(new Integer(i), client);
                self.socketStatusArray[i] = false;
            }
        }

        /**
         * 从连接池中获取一个空闲的Socket
         * @return 获取的TCP连接
         */
        public static SocketChannel getConnection() throws Exception {
            if (self == null)
                init();
            int i = 0;
            for (i = 0; i < CONNECTION_POOL_SIZE; i++) {
                if (!self.socketStatusArray[i]) {
                    self.socketStatusArray[i] = true;
                    break;
                }
            }
            if (i < CONNECTION_POOL_SIZE) {
                return self.socketPool.get(new Integer(i));

            } else {

                //目前连接池无可用连接时只是简单的新建一个连接
                SocketChannel newClient = allocateSocketChannel();
                CONNECTION_POOL_SIZE++;
                self.socketPool.put(CONNECTION_POOL_SIZE, newClient);
                return newClient;
            }
        }

        /**
         * 当获得的socket不可用时，重新获得一个空闲的socket。
         *
         * @param socket
         *            不可用的socket
         * @return 新得到的socket
         * @throws Exception
         */
        public static SocketChannel rebuildConnection(SocketChannel socket)
                throws Exception {
            if (self == null) {
                init();
            }
            SocketChannel newSocket = null;
            try {
                for (int i = 0; i < CONNECTION_POOL_SIZE; i++) {
                    if (self.socketPool.get(new Integer(i)) == socket) {
                        newSocket = allocateSocketChannel();
                        self.socketPool.put(new Integer(i), newSocket);
                        self.socketStatusArray[i] = true;
                    }
                }

            } catch (Exception e) {
                System.out.println("重建连接失败!");
                throw new RuntimeException(e);
            }
            return newSocket;
        }


        /**
         * 将用完的socket放回池中，调整为空闲状态。此时连接并没有断开。
         *
         * @param socket
         *            使用完的socket
         * @throws Exception
         */
        public static void releaseConnection(SocketChannel socket) throws Exception {
            if (self == null) {
                init();
            }
            for (int i = 0; i < CONNECTION_POOL_SIZE; i++) {
                if (self.socketPool.get(new Integer(i)) == socket) {
                    self.socketStatusArray[i] = false;
                    break;
                }
            }
        }

        /**
         * 断开池中所有连接
         *
         * @throws Exception
         */
        public synchronized static void releaseAllConnection() throws Exception {
            if (self == null)
                init();

            // 关闭所有连接
            SocketChannel socket = null;
            for (int i = 0; i < CONNECTION_POOL_SIZE; i++) {
                socket = self.socketPool.get(new Integer(i));
                try {
                    socket.close();
                    self.socketStatusArray[i] = false;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }


        public static SocketChannel allocateSocketChannel(){

            SERVER_ADDRESS = new InetSocketAddress(
                    IP, PORT);
            SocketChannel socketChannel = null;
            SocketChannel client = null;
            try{
                socketChannel = SocketChannel.open();
                socketChannel.configureBlocking(false);
                selector = Selector.open();
                socketChannel.register(selector, SelectionKey.OP_CONNECT);
                socketChannel.connect(SERVER_ADDRESS);
                Set<SelectionKey> selectionKeys;
                Iterator<SelectionKey> iterator;
                SelectionKey selectionKey;
                selector.select();
                selectionKeys = selector.selectedKeys();
                iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    selectionKey = iterator.next();
                    if (selectionKey.isConnectable()) {
                        client = (SocketChannel) selectionKey.channel();
                        if (client.isConnectionPending()) {
                            client.finishConnect();
                            client.register(selector, SelectionKey.OP_WRITE);
                            break;
                        }
                    }
                }
            }catch(Exception e){
                e.printStackTrace();
            }
            return client;
        }

        public static Selector getSelector() {
            return selector;
        }
    }

    /**
     * 发送配置
     */
    static class ServiceRequest{
        byte ProtocalNum;
        byte        FunctionNum;
        byte MessageLen;
        byte        Xmlbytes;

        public byte getProtocalNum() {
            return ProtocalNum;
        }

        public void setProtocalNum(byte protocalNum) {
            ProtocalNum = protocalNum;
        }

        public byte getFunctionNum() {
            return FunctionNum;
        }

        public void setFunctionNum(byte functionNum) {
            FunctionNum = functionNum;
        }

        public byte getMessageLen() {
            return MessageLen;
        }

        public void setMessageLen(byte messageLen) {
            MessageLen = messageLen;
        }

        public byte getXmlbytes() {
            return Xmlbytes;
        }

        public void setXmlbytes(byte xmlbytes) {
            Xmlbytes = xmlbytes;
        }
    }

    /**
     *  工作类
     */
    static class NIOSocketBackUp{
        /*缓冲区大小*/
        private static int BLOCK = 8*4096;
        /*发送数据缓冲区*/
        private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);

        /*接受数据缓冲区*/
        private static ByteBuffer protocalNum = ByteBuffer.allocate(4);
        private static ByteBuffer functionNum = ByteBuffer.allocate(4);
        private static ByteBuffer messageLen = ByteBuffer.allocate(4);
        private static ByteBuffer receivebuffer = null;
        private  SocketChannel client = null;
        private Selector selector = null;

        private boolean readable = true;
        private boolean writable = true;


        public NIOSocketBackUp() throws Exception{
            client = NIOConnectionPool.getConnection();
            selector = NIOConnectionPool.getSelector();
        }

        public String send(ServiceRequest request) throws Exception {

            Set<SelectionKey> selectionKeys;
            Iterator<SelectionKey> iterator;
            SelectionKey selectionKey;
            int count=0;
            boolean flag = true;
            String receiveText="";
            while (flag) {
                selector.select();
                //返回此选择器的已选择键集。
                selectionKeys = selector.selectedKeys();
                iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    selectionKey = iterator.next();
                    if (selectionKey.isWritable() && (writable)) {
                        sendbuffer.clear();
                        sendbuffer.put(request.getProtocalNum());
                        sendbuffer.put(request.getFunctionNum());
                        sendbuffer.put(request.getMessageLen());
                        sendbuffer.put(request.getXmlbytes());
                        sendbuffer.flip();
                        client.write(sendbuffer);
                        client.register(selector, SelectionKey.OP_READ);
                        writable = false;
                    } else if (selectionKey.isReadable() && (readable) ) {
                        protocalNum.clear();
                        functionNum.clear();
                        messageLen.clear();


                        count=client.read(protocalNum);
                        count=client.read(functionNum);
                        count=client.read(messageLen);
                        messageLen.rewind();
                        int length = messageLen.asIntBuffer().get(0);
                        receivebuffer = ByteBuffer.allocate(length-12);
                        receivebuffer.clear();

                        //read方式竟然不阻塞
                        int total=0;
                        while(total!=(length-12)){
                            count=client.read(receivebuffer);
                            total+=count;
                        }
                        client.register(selector, SelectionKey.OP_WRITE);
                        receiveText = new String(receivebuffer.array(),"GBK");
                        flag = false;
                        readable = false;
                        break;
                    }
                }
            }


            NIOConnectionPool.releaseConnection(client);
            return receiveText.trim();
        }
    }

}
